package org.gdstash.item;

import org.gdstash.db.DBItem;
import org.gdstash.db.DBStashItem;
import org.gdstash.db.ItemClass;
import org.gdstash.file.GDFileSize;
import org.gdstash.file.GDParseException;
import org.gdstash.file.GDReader;
import org.gdstash.file.GDWriter;
import org.gdstash.util.*;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

public class GDTransmute implements GDFileSize {
    public static final int ITEM_TYPE_UNSUPPORTED = -1;
    public static final int ITEM_TYPE_HEAD = 1;
    public static final int ITEM_TYPE_TORSO = 3;
    public static final int ITEM_TYPE_LEGS = 4;
    public static final int ITEM_TYPE_FEET = 5;
    public static final int ITEM_TYPE_HANDS = 7;
    public static final int ITEM_TYPE_OFFHAND = 8;
    public static final int ITEM_TYPE_WEAPON = 9;
    public static final int ITEM_TYPE_SHOULDERS = 14;
    public static final int ITEM_TYPE_MEDAL = 15;
    private static final int VERSION_1 = 1;
    private static final int VERSION_2 = 2;
    private File file;
    private int key;
    private int version;
    private boolean fileError;
    private String modName;
    private byte expansionStatus;
    private String filename;
    private boolean hardcore;
    private List<GDTransmuteType> types;

    public GDTransmute(final File file) {
        this.file = file;
        this.fileError = false;
        this.filename = file.getName().toUpperCase(GDConstants.LOCALE_US);
        this.hardcore = false;
        if (this.filename.endsWith(".BSH")) {
            this.hardcore = true;
        }
        if (this.filename.endsWith(".CSH")) {
            this.hardcore = true;
        }
        if (this.filename.endsWith(".GSH")) {
            this.hardcore = true;
        }
        this.types = new LinkedList<GDTransmuteType>();
    }

    public boolean hasErrors() {
        return this.fileError;
    }

    public void insertCollectedItems() {
        final List<DBItem> list = DBStashItem.getAllDistinct(this.hardcore);
        this.insertItems(list);
    }

    public void insertAllItems() {
        final List<DBItem> list = DBItem.getAll();
        this.insertItems(list);
    }

    private void insertItems(final List<DBItem> list) {
        final List<DBItem> condensed = this.condenseItems(list);
        for (final DBItem dbi : condensed) {
            this.insertItem(dbi);
        }
    }

    private static boolean containsItem(final List<DBItem> list, final DBItem item) {
        if (item == null) {
            return true;
        }
        final String meshID = item.getMeshID();
        final String baseID = item.getBaseTextureID();
        final String bumpID = item.getBumpTextureID();
        final String glowID = item.getGlowTextureID();
        final String shaderID = item.getShaderID();
        if (meshID == null) {
            return true;
        }
        boolean found = false;
        for (final DBItem dbi : list) {
            if (dbi != null && meshID.equals(dbi.getMeshID())) {
                final String base2 = dbi.getBaseTextureID();
                final String bump2 = dbi.getBumpTextureID();
                final String glow2 = dbi.getGlowTextureID();
                final String shader2 = dbi.getShaderID();
                boolean baseFound = false;
                boolean bumpFound = false;
                boolean glowFound = false;
                boolean shaderFound = false;
                if (baseID == null) {
                    if (base2 == null) {
                        baseFound = true;
                    }
                } else if (baseID.equals(base2)) {
                    baseFound = true;
                }
                if (bumpID == null) {
                    if (bump2 == null) {
                        bumpFound = true;
                    }
                } else if (bumpID.equals(bump2)) {
                    bumpFound = true;
                }
                if (glowID == null) {
                    if (glow2 == null) {
                        glowFound = true;
                    }
                } else if (glowID.equals(glow2)) {
                    glowFound = true;
                }
                if (shaderID == null) {
                    if (shader2 == null) {
                        shaderFound = true;
                    }
                } else if (shaderID.equals(shader2)) {
                    shaderFound = true;
                }
                if (baseFound && bumpFound && glowFound && shaderFound) {
                    found = true;
                    break;
                }
                continue;
            }
        }
        return found;
    }

    private List<DBItem> condenseItems(final List<DBItem> list) {
        final List<DBItem> condensed = new LinkedList<DBItem>();
        Collections.<DBItem>sort(list, new SortItemByPicLevel());
        for (final DBItem dbi : list) {
            if (dbi.isEnemyOnly()) {
                continue;
            }
            final int type = ItemClass.getTransmuteType(dbi.getItemClass());
            if (type == -1) {
                continue;
            }
            final String meshID = dbi.getMeshID();
            if (meshID == null) {
                continue;
            }
            final boolean found = containsItem(condensed, dbi);
            if (found) {
                continue;
            }
            condensed.add(dbi);
        }
        return condensed;
    }

    private void insertItem(final DBItem item) {
        final int type = ItemClass.getTransmuteType(item.getItemClass());
        boolean found = false;
        for (final GDTransmuteType tt : this.types) {
            if (tt.getTransmuteType() == type) {
                found = true;
                tt.insertItem(item);
                break;
            }
        }
        if (!found) {
            final GDTransmuteType tt2 = new GDTransmuteType(type);
            tt2.insertItem(item);
            this.types.add(tt2);
        }
    }

    private void removeItem(final DBItem item) {
        final int type = ItemClass.getTransmuteType(item.getItemClass());
        boolean found = false;
        for (final GDTransmuteType tt : this.types) {
            if (tt.getTransmuteType() == type) {
                found = true;
                tt.removeItem(item);
                break;
            }
        }
    }

    public void read(final GDLog log) {
        String path = null;
        int val = 0;
        try {
            path = this.file.getCanonicalPath();
            GDReader.readEncFileToBuffer(this.file);
            this.key = GDReader.readKey();
            val = GDReader.readEncInt(true);
            if (val != 1) {
                throw new GDParseException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"), 0L);
            }
            final GDReader.Block block = new GDReader.Block();
            val = GDReader.readBlockStart(block);
            if (val != 19) {
                throw new GDParseException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"), 0L);
            }
            this.version = GDReader.readEncInt(true);
            if (this.version != 1 && this.version != 2) {
                throw new FileVersionException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"));
            }
            val = GDReader.readEncInt(false);
            if (val != 0) {
                throw new GDParseException(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "ERR_UNSUPPORTED_VERSION"), 0L);
            }
            this.modName = GDReader.readEncString();
            if (this.version >= 2) {
                this.expansionStatus = GDReader.readEncByte();
            }
            final int numTypes = GDReader.readEncInt(true);
            this.types.clear();
            for (int i = 0; i < numTypes; ++i) {
                final GDTransmuteType tt = new GDTransmuteType();
                tt.read(log);
                this.types.add(tt);
            }
            GDReader.readBlockEnd(block);
        } catch (FileNotFoundException ex) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_NOT_FOUND", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addError(ex);
            } else {
                log.addError(ex);
            }
            this.fileError = true;
        } catch (FileVersionException ex2) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addInfo(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "INFO_UPDATE_GDSTASH"));
            } else {
                log.addInfo(GDMsgFormatter.getString(GDMsgFormatter.rbMsg, "INFO_UPDATE_GDSTASH"));
            }
            if (log == null) {
                GDMsgLogger.addError(ex2);
            } else {
                log.addError(ex2);
            }
            this.fileError = true;
        } catch (Exception ex3) {
            if (path != null) {
                final Object[] args = {path};
                final String msg = GDMsgFormatter.format(GDMsgFormatter.rbMsg, "ERR_FILE_READ", args);
                if (log == null) {
                    GDMsgLogger.addError(msg);
                } else {
                    log.addError(msg);
                }
            }
            if (log == null) {
                GDMsgLogger.addError(ex3);
            } else {
                log.addError(ex3);
            }
            this.fileError = true;
        }
    }

    @Override
    public int getByteSize() {
        int size = 0;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        size += 4;
        if (this.modName != null) {
            size += this.modName.length();
        }
        if (this.version >= 2) {
            ++size;
        }
        size += 4;
        for (final GDTransmuteType tt : this.types) {
            size += tt.getByteSize();
        }
        size += 4;
        return size;
    }

    private void createBuffer() throws IOException {
        GDWriter.reserveBuffer(this);
        GDWriter.writeInt(1431655765);
        GDWriter.writeInt(1);
        final GDReader.Block block = new GDReader.Block();
        GDWriter.writeBlockStart(block, 19);
        GDWriter.writeInt(this.version);
        GDWriter.writeInt(0);
        GDWriter.writeString(this.modName);
        if (this.version >= 2) {
            GDWriter.writeByte(this.expansionStatus);
        }
        GDWriter.writeInt(this.types.size());
        for (final GDTransmuteType tt : this.types) {
            tt.write();
        }
        GDWriter.writeBlockEnd(block);
    }

    public void write() throws IOException {
        this.createBuffer();
        final String path = this.file.getParent() + GDConstants.FILE_SEPARATOR;
        String tname = "transmutes.t";
        if (this.filename.endsWith(".BST")) {
            tname = "transmutes.bt";
        }
        if (this.filename.endsWith(".BSH")) {
            tname = "transmutes.bh";
        }
        if (this.filename.endsWith(".CST")) {
            tname = "transmutes.ct";
        }
        if (this.filename.endsWith(".CSH")) {
            tname = "transmutes.ch";
        }
        if (this.filename.endsWith(".GSH")) {
            tname = "transmutes.h";
        }
        final File temp = new File(path + "temp_tmp.tmp");
        temp.createNewFile();
        GDWriter.writeBuffer(temp);
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int i = 9; i >= 0; --i) {
            fCurr = new File(path + tname + String.format("%02d", i));
            fNew = new File(path + tname + String.format("%02d", i + 1));
            if (fCurr.exists()) {
                if (i == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        this.file.renameTo(fCurr);
        temp.renameTo(this.file);
    }

    public void write2() throws IOException {
        final String path = this.file.getParent() + GDConstants.FILE_SEPARATOR;
        String tname = "transmutes.t";
        if (this.filename.endsWith(".BST")) {
            tname = "transmutes.bt";
        }
        if (this.filename.endsWith(".BSH")) {
            tname = "transmutes.bh";
        }
        if (this.filename.endsWith(".CST")) {
            tname = "transmutes.ct";
        }
        if (this.filename.endsWith(".CSH")) {
            tname = "transmutes.ch";
        }
        if (this.filename.endsWith(".GSH")) {
            tname = "transmutes.h";
        }
        File fCurr = new File(path + tname + String.format("%02d", 0));
        File fNew = null;
        for (int i = 9; i >= 0; --i) {
            fCurr = new File(path + tname + String.format("%02d", i));
            fNew = new File(path + tname + String.format("%02d", i + 1));
            if (fCurr.exists()) {
                if (i == 9) {
                    fCurr.delete();
                } else {
                    fCurr.renameTo(fNew);
                }
            }
        }
        GDWriter.copyFile(this.file, fCurr);
        try {
            this.createBuffer();
            GDWriter.writeBuffer(this.file);
        } catch (IOException ex) {
            GDWriter.copyFile(fCurr, this.file);
            throw ex;
        }
    }

    private static class SortItemByPicLevel implements Comparator<DBItem> {
        @Override
        public int compare(final DBItem i1, final DBItem i2) {
            if (i1 == null) {
                return 1;
            }
            if (i2 == null) {
                return -1;
            }
            final int level = i1.getItemLevel() - i2.getItemLevel();
            if (level != 0) {
                return level;
            }
            final String mesh1 = i1.getMeshID();
            final String mesh2 = i2.getMeshID();
            if (mesh1 == null && mesh2 != null) {
                return 1;
            }
            if (mesh1 != null && mesh2 == null) {
                return -1;
            }
            if (mesh1 != null && mesh2 != null) {
                final int mesh3 = mesh1.compareTo(mesh2);
                if (mesh3 != 0) {
                    return mesh3;
                }
            }
            final String base1 = i1.getBaseTextureID();
            final String base2 = i2.getBaseTextureID();
            if (base1 == null && base2 != null) {
                return -1;
            }
            if (base1 != null && base2 == null) {
                return 1;
            }
            if (base1 != null && base2 != null) {
                final int base3 = base1.compareTo(base2);
                if (base3 != 0) {
                    return base3;
                }
            }
            final String bump1 = i1.getBumpTextureID();
            final String bump2 = i2.getBumpTextureID();
            if (bump1 == null && bump2 != null) {
                return -1;
            }
            if (bump1 != null && bump2 == null) {
                return 1;
            }
            if (bump1 != null && bump2 != null) {
                final int bump3 = bump1.compareTo(bump2);
                if (bump3 != 0) {
                    return bump3;
                }
            }
            final String glow1 = i1.getGlowTextureID();
            final String glow2 = i2.getGlowTextureID();
            if (glow1 == null && glow2 != null) {
                return -1;
            }
            if (glow1 != null && glow2 == null) {
                return 1;
            }
            if (glow1 != null && glow2 != null) {
                final int glow3 = glow1.compareTo(glow2);
                if (glow3 != 0) {
                    return glow3;
                }
            }
            final String shader1 = i1.getShaderID();
            final String shader2 = i2.getShaderID();
            if (shader1 == null && shader2 != null) {
                return -1;
            }
            if (shader1 != null && shader2 == null) {
                return 1;
            }
            if (shader1 != null && shader2 != null) {
                final int shader3 = shader1.compareTo(shader2);
                if (shader3 != 0) {
                    return shader3;
                }
            }
            final int level2 = i1.getRequiredlevel();
            final int level3 = i2.getRequiredlevel();
            final int delta = level2 - level3;
            if (delta != 0) {
                return delta;
            }
            final int itemID = i1.getItemID().compareTo(i2.getItemID());
            return itemID;
        }
    }
}
